<template>
  <div class="upload-file">
    <div class="file-upload-container" v-bind="$attrs" @click="handleBtnClick">
      <slot v-if="fileStatus === 'ready'" name="ready">
        <button class="btn btn-primary">点击上传</button>
      </slot>
      <slot v-else-if="fileStatus === 'uploading'" name="uploading">
        <button class="btn btn-primary">正在上传</button>
      </slot>
      <slot v-else-if="fileStatus === 'success'" name="uploaded" :uploadData="uploadData">
        <button class="btn btn-primary">上传成功</button>
      </slot>
    </div>
    <input type="file" ref="inputFileRef" class="d-none" @change="handleFileChange" />
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue'
import axios from 'axios'
import mitt from 'mitt'
import { PostProps } from '@/store'
export const emitter = mitt()
type fileStatusProps = 'ready' | 'uploading' | 'success' | 'error'
type BeforeUpload = (file: File) => boolean
export interface ResponseType<p> {
  code: number
  msg: string
  data: p
}
export default defineComponent({
  inheritAttrs: false,
  props: {
    action: {
      type: String as PropType<fileStatusProps>,
      require: true
    },
    beforeUpload: Function as PropType<BeforeUpload>,
    editUploadedData: Object as PropType<PostProps>
  },
  emits: ['file-uploaded', 'file-uploaded-error'],
  setup (props, context) {
    const inputFileRef = ref<null | HTMLInputElement>(null)
    const fileStatus = ref<fileStatusProps>(props.editUploadedData ? 'success' : 'ready')
    const uploadData = ref<unknown>(props.editUploadedData)
    /* 父组件传入的editUploadedData属性是异步的 , 所以一开始是undefined ,
    但是由于后来异步请求成功 , editUploadedData被改变, 但是setup函数只执行了一次,
    后续的editUploadedData属性被改变的时候并没有触发setup函数, editUploadedData的值就没有改变
    所以要通过watch监听这个属性, 来达到响应式变换 */
    watch(() => props.editUploadedData, (newValue) => {
      if (newValue) {
        uploadData.value = newValue
        fileStatus.value = 'success'
      }
    })
    const handleBtnClick = () => {
      if (inputFileRef.value) {
        inputFileRef.value.click()
      }
    }
    const handleFileChange = (e: Event) => {
      const target = e.target as HTMLInputElement
      if (target.files) {
        const files = Array.from(target.files)
        const uploadFile = files[0]
        if (props.beforeUpload) {
          const beforeUpload = props.beforeUpload
          const result = beforeUpload(uploadFile)
          if (!result) {
            return false
          }
        }
        const formData = new FormData()
        formData.append(uploadFile.name, uploadFile)
        fileStatus.value = 'uploading'
        axios
          .post('api/upload', formData, {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          })
          .then(resp => {
            context.emit('file-uploaded', resp.data)
            fileStatus.value = 'success'
            uploadData.value = resp.data
          })
          .catch(e => {
            emitter.emit('tooBig')
            context.emit('file-uploaded-error', e)
            fileStatus.value = 'error'
          })
          .finally(() => {
            if (inputFileRef.value) {
              inputFileRef.value.value = ''
            }
          })
      }
    }
    return {
      inputFileRef,
      handleBtnClick,
      handleFileChange,
      fileStatus,
      uploadData
    }
  }
})
</script>
